#pragma once

#include <iostream>
#include <memory>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include "err.hpp"
#include "ThreadPool.hpp"
#include "log.hpp"
#include "daemon.hpp"

namespace ns_server
{
    static const uint16_t defaultport = 8081;
    static const int backlog = 32;

    using func_t = std::function<std::string(const std::string &)>;

    class TcpServer;
    class ThreadData
    {
    public:
        ThreadData(int sock, const std::string &ip, const uint16_t &port, TcpServer *ts)
            : _sock(sock), _clientip(ip), _clientport(port), _current(ts)
        {
        }

    public:
        int _sock;
        std::string _clientip;
        uint16_t _clientport;
        TcpServer *_current;
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = defaultport) : _func(func), _port(port), _quit(true)
        {
        }

        void initServer()
        {
            // 创建socket接口，打开网络文件
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                //std::cerr << "create socket error" << std::endl;
                logMessage(Fatal, "create socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(SOCKET_ERR);
            }
            logMessage(Info, "create socket success, code: %d, error string: %s", errno, strerror(errno));

            // bind
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = htonl(INADDR_ANY);
            if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                //std::cerr << "bind socket error" << std::endl;
                logMessage(Fatal, "bind socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(BIND_ERR);
            }
            logMessage(Info, "bind socket success, code: %d, error string: %s", errno, strerror(errno));

            // 监听
            if (listen(_listensock, backlog) < 0)
            {
                //std::cerr << "lisent socket error" << std::endl;
                logMessage(Fatal, "lisent socket error, code: %d, error string: %s", errno, strerror(errno));
                exit(LISTEN_ERR);
            }
            logMessage(Info, "lisent socket success, code: %d, error string: %s", errno, strerror(errno));
        }

        void start()
        {
            // signal(SIGCHLD, SIG_IGN);//最推荐的做法，对child的信号进行忽略，无需等待child
            // 收到child的信号时捕捉对他的处理方法，易信号的方式进行回收，也不太推荐，有更好的做法
            // signal(SIGCHLD, handler);
            _quit = false;
            while (!_quit)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                // 获取连接，accept
                int sock = accept(_listensock, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    //std::cerr << "accept error" << std::endl;
                    logMessage(Warning, "accept error, code: %d, error string: %s", errno, strerror(errno));
                    continue;
                }

                // 提取客户端信息
                std::string clientip = inet_ntoa(client.sin_addr); // 注意把网络序列格式的32位整数转化成字符串形式
                uint16_t clientport = ntohs(client.sin_port);      // 注意把网络序列格式转化成主机格式

                // 成功获取新连接,开始进行业务处理
                // std::cout << "成功获取新连接：" << sock << "from " << _listensock << ","
                //           << clientip << "-" << clientport << std::endl;
                logMessage(Info, "成功获取新连接：%d from %d, who:%s - %d", sock, _listensock, clientip.c_str(), clientport);

                // v1
                // service(sock, clientip, clientport);

                // v2:多进程版
                //  pid_t id = fork();
                //  if (id < 0)
                //  {
                //      close(sock);
                //      continue;
                //  }
                //  else if (id == 0)//child会继承father的fd
                //  {
                //      //建议关闭不需要的fd，以防对其进行误操作
                //      close(_listensock);
                //      if (fork() > 0)//创建孙子进程继承子进程的fd
                //      {
                //          exit(0);//子进程退出，使得孙子进程变成孤儿进程，交由操作系统领养
                //      }
                //      service(sock, clientip, clientport);//业务处理给孙子进程处理
                //      exit(0);//孙子进程退出后由操作系统自动回收
                //  }
                //  //father一定要关闭不需要的fd，防止fd泄漏
                //  close(sock);
                //  pid_t ret = waitpid(id, nullptr, 0);//这是阻塞等待！
                //  //非阻塞等待,也不推荐,因为如果子进程过多时，父进程无法获取连接会在那里阻塞，会无法去调用waitpid()
                //  //pid_t ret = waitpid(id, nullptr, WNOHANG)
                //  if (ret == id)
                //  {
                //      std::cout << "wait child " << id << "success" << std::endl;
                //  }

                // v3:多线程版本 -- 原生多线程
                // pthread_t tid;
                // ThreadData *td = new ThreadData(sock, clientip, clientport, this);
                // pthread_create(&tid, nullptr, threadRoutine, td);

                // v4:多线程--线程池版本
                // 使用线程池的时候，因为线程个数有限，所以一定要用来处理短任务
                Task t(sock, clientip, clientport, std::bind(&TcpServer::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                ThreadPool<Task>::getInstance()->pushTask(t);
            }
        }

        static void *threadRoutine(void *args)
        {
            pthread_detach(pthread_self());

            ThreadData *td = static_cast<ThreadData *>(args);
            td->_current->service(td->_sock, td->_clientip, td->_clientport);
            delete td;
            return nullptr;
        }

        void service(int sock, const std::string &clientip, const uint16_t &clientport)
        {
            std::string who = clientip + "-" + std::to_string(clientport);
            char buffer[1024];

            // 长任务版
            //  while (true)
            //  {
            //      ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
            //      if (s > 0)
            //      {
            //          buffer[s] = '\0';
            //          std::string res = _func(buffer); // 进行回调
            //          std::cout << who << ">> " << res << std::endl;

            //         write(sock, res.c_str(), res.size());
            //     }
            //     else if (s == 0)
            //     {
            //         // 对方将连接关闭了
            //         close(sock);
            //         std::cout << who << " quit, me too" << std::endl;
            //         break;
            //     }
            //     else
            //     {
            //         close(sock);
            //         std::cout << "read error: " << strerror(errno) << std::endl;
            //         break;
            //     }
            // }

            // 短任务版
            ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
            std::cout << s;
            if (s > 0)
            {
                buffer[s] = '\0';
                std::string res = _func(buffer); // 进行回调
                //std::cout << who << ">> " << res << std::endl;
                logMessage(Debug, "%s# %s", who.c_str(), res.c_str());
                write(sock, res.c_str(), res.size());   
            }
            else if (s == 0)
            {
                // 对方将连接关闭了
                //std::cout << who << " quit, me too" << std::endl;
                logMessage(Info, "%s quit, me too", who.c_str());
            }
            else
            {
                //std::cout << "read error: " << strerror(errno) << std::endl;
                logMessage(Error, "read error: %d:%s", errno, strerror(errno));
            }
            close(sock);
        }

        ~TcpServer()
        {
        }

    private:
        uint16_t _port;
        int _listensock;
        bool _quit;
        func_t _func;
    };
}
